package com.skyyan.netty.jjf.protocol;

import cn.hutool.core.util.ByteUtil;
import com.skyyan.netty.jjf.util.BcdUtil;
import com.skyyan.netty.jjf.util.ChineseUtils;

import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;


/**
 * C.7 充值报文
 * AFN = 0x95
 *
 * 上行报文格式（设备 → 中心）：
 * 起始字符0x68
 * 长度1个字节
 * 起始字符0x68
 * - 控制域C：0x01   上行
 * - 地址域：19个字节
 *   地址域[0]-[15]  设备注册号 16进制,低位在前 高位在后
 *   地址域[16]-[18]  充值管理机序列号 16进制,低位在前 高位在后
 * - AFN(1字节)：0x95
 * - 数据域28个字节
 *   数据域[0]-[15]  16个字节 农户注册号 16进制,低位在前 高位在后
 *   数据域[16]-[19] 4个字节 充值金额 16进制,低位在前 高位在后，单位是分 ；元*100
 *   数据域[20]-[27] 8个字节 BCD码 订单号 16进制
 * - CRC8校验码(1字节)
 * -结束字符 0x16
 *
 * 下行报文格式（中心 →设备） :
 * 起始字符0x68
 * 长度1个字节
 * 起始字符0x68
 * - 控制域C：0x00   下行
 * - AFN(1字节)：0x95
 * - 数据域 25个字节
 *   数据域[0]    1个字节 BCD码；00启用，01禁用 02隶属信息不符 03无次卡信息 ,04其他
 *   数据域[1]-[4] 余额 4字节 16进制 低位在前 高位在后，单位为分
 *   数据域[5]-[16] 12个字节 姓名 16进制UTF-8编码，每三个字节一个中文
 *   数据域[17]-[24] 8个字节 BCD码 订单号
 * - CRC8校验码(1字节)
 * -结束字符 0x16
 * @author skyyan
 */

public class RechargeMessage extends Message {

    /**
     * 上行充值报文（设备 → 中心）
     * 需要参数：设备注册号、设备序列号、农户注册号、充值金额、订单号
     */
    public static class Uplink extends RechargeMessage {
        // 设备注册号 (16字节，低位在前)
        private byte[] deviceRegIdBytes;
        // 设备序列号 (3字节，低位在前)
        private byte[] deviceSerialNoBytes;
        // 农户注册号 (16字节，低位在前)
        private byte[] farmerRegIdBytes;
        // 充值金额 (4字节，小端序，单位为分)
        private int rechargeAmount;
        // 订单号 (8字节BCD码)
        private String orderId;
        //BCD码 订单号
        private byte[] orderIdBytes;


        /**
         * 构造一个上行的充值报文
         * @param deviceRegId 设备注册号 (16字节)
         * @param deviceSerialNo 设备序列号 (int类型)
         * @param farmerRegistrationId 农户注册编号 (16字节)
         * @param rechargeAmount 充值金额 (分为单位)
         * @param orderId 订单号 (8字节BCD码)
         */
        public Uplink(byte[] deviceRegId, int deviceSerialNo, byte[] farmerRegistrationId,
                     int rechargeAmount, String orderId) {
            super(FrameConstants.CONTROL_UPLINK,
                  createAddress(deviceRegId, deviceSerialNo),
                  createUserData(farmerRegistrationId, rechargeAmount, orderId));

            this.deviceRegIdBytes = deviceRegId != null ? deviceRegId.clone() : new byte[16];
            this.deviceSerialNoBytes = new byte[3];
            this.deviceSerialNoBytes[0] = (byte) (deviceSerialNo & 0xFF);
            this.deviceSerialNoBytes[1] = (byte) ((deviceSerialNo >> 8) & 0xFF);
            this.deviceSerialNoBytes[2] = (byte) ((deviceSerialNo >> 16) & 0xFF);
            this.farmerRegIdBytes = farmerRegistrationId != null ? farmerRegistrationId.clone() : new byte[16];
            this.rechargeAmount = rechargeAmount;
            this.orderId = orderId;
        }

        /**
         * 供解码器使用的构造函数
         * @param address 地址域 (19字节)
         * @param userData 用户数据 (28字节)
         */
        public Uplink(byte[] address, byte[] userData) {
            super(FrameConstants.CONTROL_UPLINK, address, userData);

            // 从地址域中提取设备注册号和设备序列号
            if (address != null && address.length == 19) {
                this.deviceRegIdBytes = new byte[16];
                this.deviceSerialNoBytes = new byte[3];
                // 修复数组拷贝范围
                System.arraycopy(address, 0, this.deviceRegIdBytes, 0, 16);
                System.arraycopy(address, 16, this.deviceSerialNoBytes, 0, 3);

            }

            // 从用户数据中提取其他参数
            if (userData != null && userData.length == 28) {
                //农户注册编号
//                this.farmerRegIdBytes =new byte[16];
//                System.arraycopy(userData, 0, this.farmerRegIdBytes, 0, 16);

                this.farmerRegIdBytes = Arrays.copyOfRange(userData, 0, 16);
//                byte[] money=new byte[4];
//                System.arraycopy(userData, 16, money, 0, 4);
                //数据域[16]-[19] =共4个字节
                byte[] money=Arrays.copyOfRange(userData, 16, 16+4);
                // 修复：正确处理3字节转换为int
                this.rechargeAmount  = ByteUtil.bytesToInt(money, ByteOrder.LITTLE_ENDIAN);
                // 订单号是BCD码，暂时以字符串形式保存
                this.orderId =  BcdUtil.bcdToString(Arrays.copyOfRange(userData, 20, 20+8));
//                this.orderId = new String(Arrays.copyOfRange(userData, 20, 20+8), StandardCharsets.US_ASCII);
            }
        }

        /**
         * 创建地址域
         * @param deviceRegId 设备注册号 (16字节)
         * @param deviceSerialNo 设备序列号 (int类型)
         * @return 19字节地址域
         */
        private static byte[] createAddress(byte[] deviceRegId, int deviceSerialNo) {
            byte[] address = new byte[19];

            // 设备注册号 (16字节，低位在前)
            if (deviceRegId != null) {
                System.arraycopy(deviceRegId, 0, address, 0, Math.min(deviceRegId.length, 16));
            }

            // 设备序列号 (3字节，低位在前)
            address[16] = (byte) (deviceSerialNo & 0xFF);
            address[17] = (byte) ((deviceSerialNo >> 8) & 0xFF);
            address[18] = (byte) ((deviceSerialNo >> 16) & 0xFF);

            return address;
        }

        /**
         * 创建用户数据
         * @param farmerRegistrationId 农户注册编号 (16字节)
         * @param rechargeAmount 充值金额 (分为单位)
         * @param orderId 订单号 (8字节)
         * @return 28字节的用户数据
         */
        private static byte[] createUserData(byte[] farmerRegistrationId, int rechargeAmount, String orderId) {
            if (farmerRegistrationId == null || farmerRegistrationId.length != 16) {
                throw new IllegalArgumentException("农户注册编号必须为16字节");
            }

            byte[] userData = new byte[28];

            // 农户注册编号 (16字节)
            System.arraycopy(farmerRegistrationId, 0, userData, 0, 16);

            // 充值金额 (4字节，小端字节序)
            byte[] amountBytes = ByteUtil.intToBytes(rechargeAmount);
            System.arraycopy(amountBytes, 0, userData, 16, 4);

            // 订单号 (8字节)
            if (orderId != null) {
                byte[] orderIdBytes = orderId.getBytes(StandardCharsets.US_ASCII);
                System.arraycopy(orderIdBytes, 0, userData, 20, Math.min(orderIdBytes.length, 8));
            }

            return userData;
        }

        /**
         * 获取设备注册号
         * @return 设备注册号 (16字节，低位在前)
         */
        public byte[] getDeviceRegId() {
            return deviceRegIdBytes != null ? deviceRegIdBytes.clone() : new byte[16];
        }

        /**
         * 获取设备序列号
         * @return 设备序列号 (3字节，低位在前)
         */
        public byte[] getDeviceSerialNo() {
            return deviceSerialNoBytes != null ? deviceSerialNoBytes.clone() : new byte[3];
        }

        /**
         * 获取设备序列号 (int类型)
         * @return 设备序列号
         */
        public int getDeviceSerialNoInt() {
            if (deviceSerialNoBytes != null && deviceSerialNoBytes.length >= 3) {
                return (deviceSerialNoBytes[0] & 0xFF) |
                       ((deviceSerialNoBytes[1] & 0xFF) << 8) |
                       ((deviceSerialNoBytes[2] & 0xFF) << 16);
            }
            return 0;
        }

        /**
         * 获取农户注册号
         * @return 农户注册号 (16字节，低位在前)
         */
        public byte[] getFarmerRegId() {
            return farmerRegIdBytes != null ? farmerRegIdBytes.clone() : new byte[16];
        }

        /**
         * 获取充值金额
         * @return 充值金额 (分为单位)
         */
        public int getRechargeAmount() {
            return rechargeAmount;
        }

        /**
         * 获取订单号
         * @return 订单号
         */
        public String getOrderId() {
            return orderId;
        }

        @Override
        public String toString() {
            return "RechargeMessage.Uplink{" +
                    "control=0x" + String.format("%02X", getControl()) +
                    ", afn=0x" + String.format("%02X", getAfn()) +
                    ", deviceRegId=" + bytesToHex(getDeviceRegId()) +
                    ", deviceSerialNo=" + bytesToHex(getDeviceSerialNo()) +
                    ", deviceSerialNoInt=0x" + String.format("%06X", getDeviceSerialNoInt()) +
                    ", farmerRegId=" + bytesToHex(getFarmerRegId()) +
                    ", rechargeAmount=" + rechargeAmount +
                    ", orderId='" + orderId + '\'' +
                    '}';
        }
    }

    /**
     * 下行充值报文（中心 → 设备）
     * 需要参数：状态码、余额、姓名、订单号
     */
    public static class Downlink extends RechargeMessage {
        // 状态 数据域[0] BCD码
        private byte statusCodeBCD;
        // 余额 数据域[1]-[4] 4字节 16进制 低位在前 高位在后，单位为分
        private byte[] balanceLSB;
        // 姓名 数据域[5]-[16] 12个字节 姓名 16进制UTF-8编码，每三个字节一个中文 ;位数不足 后边补0
        private byte[] usernameBytes;
        private String username;
        // 订单号 数据域[17]-[24] 8个字节 BCD码
        private byte[] orderNoBCD;

        /**
         * 构造一个下行的充值报文
         * @param statusCodeBCD 状态码
         * @param balance 余额(单位为分)
         * @param username 用户名
         * @param orderNo 订单号(BCD码)
         */
        public Downlink(byte statusCodeBCD, int balance, String username, String orderNo)throws Exception {
            super(FrameConstants.CONTROL_DOWNLINK, null, createUserData(statusCodeBCD, balance, username, orderNo));
            this.statusCodeBCD = statusCodeBCD;
            this.balanceLSB = new byte[4];
            byte[] balanceBytes = ByteUtil.intToBytes(balance); // 小端序
            System.arraycopy(balanceBytes, 0, this.balanceLSB, 0, 4);
            this.username=username;
            this.usernameBytes=username!=null? ChineseUtils.chineseToBytes(username): new byte[12];
            this.orderNoBCD = orderNo != null ? createOrderNoBCD(orderNo) : new byte[8];
        }

        /**
         * 供解码器使用的构造函数
         * @param address 地址域
         * @param userData 用户数据
         */
        public Downlink(byte[] address, byte[] userData) {
            super(FrameConstants.CONTROL_DOWNLINK, address, userData);
            if (userData != null) {
                if (userData.length < 25) {
                    throw new IllegalArgumentException("解析充值报文下发数据长度小于25");
                }
                this.statusCodeBCD = userData[0]; // 数据域状态
                this.balanceLSB = Arrays.copyOfRange(userData, 1, 1+4); // 余额 数据域[1]-[4] 低位在前，高位在后
                this.usernameBytes= Arrays.copyOfRange(userData, 5, 5+12); // 姓名 数据域[5]-[16] 三个字节一个中文
                this.username=ChineseUtils.bytesToHex(this.usernameBytes);
                this.orderNoBCD = Arrays.copyOfRange(userData, 17, 17+8); // 订单号 数据域[17]-[24] BCD码
            }
        }

        /**
         * 创建用户数据
         * @param statusCodeBCD 状态码
         * @param balance 余额(单位为分)
         * @param username 用户名
         * @param orderNo 订单号
         * @return 用户数据
         */
        private static byte[] createUserData(byte statusCodeBCD, int balance, String username, String orderNo)throws Exception {
            byte[] userData = new byte[25];
            userData[0] = statusCodeBCD;

            // 余额 4字节 小端序
            byte[] balanceBytes = ByteUtil.intToBytes(balance);
            System.arraycopy(balanceBytes, 0, userData, 1, 4);

            // 姓名 12字节 UTF-8编码
            if (username != null) {
                byte[] nameBytes=ChineseUtils.chineseToBytes(username);
                System.arraycopy(nameBytes, 0, userData, 5, Math.min(nameBytes.length, 12));
            }

            // 订单号 8字节 BCD码
            if (orderNo != null) {
                byte[] orderNoBytes = createOrderNoBCD(orderNo);
                System.arraycopy(orderNoBytes, 0, userData, 17, 8);
            }

            return userData;
        }

        /**
         * 创建订单号BCD字节数组（8字节）
         * @param orderNo 订单号字符串
         * @return 8字节订单号BCD数组
         */
        private static byte[] createOrderNoBCD(String orderNo) {
            byte[] orderNoBytes = new byte[8];
            if (orderNo != null) {
                byte[] inputBytes = orderNo.getBytes(StandardCharsets.US_ASCII);
                // 复制到订单号字节数组中，如果长度不足8字节，则高位补0
                int copyLength = Math.min(inputBytes.length, 8);
                System.arraycopy(inputBytes, 0, orderNoBytes, 8 - copyLength, copyLength);
            }
            return orderNoBytes;
        }

        /**
         * 获取状态码
         * @return 状态码
         */
        public byte getStatusCodeBCD() {
            return statusCodeBCD;
        }

        /**
         * 获取余额字节数组
         * @return 余额字节数组
         */
        public byte[] getBalanceLSB() {
            return balanceLSB != null ? balanceLSB.clone() : new byte[4];
        }

        /**
         * 获取余额值(单位为分)
         * @return 余额值
         */
        public int getBalance() {
            return balanceLSB != null ? ByteUtil.bytesToInt(balanceLSB) : 0;
        }

        public String getUsername() {
            return ChineseUtils.byteToChinese(this.usernameBytes);
        }

        public void setStatusCodeBCD(byte statusCodeBCD) {
            this.statusCodeBCD = statusCodeBCD;
        }

        public void setBalanceLSB(byte[] balanceLSB) {
            this.balanceLSB = balanceLSB;
        }

        public byte[] getUsernameBytes() {
            return usernameBytes;
        }

        public void setUsernameBytes(byte[] usernameBytes) {
            this.usernameBytes = usernameBytes;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public void setOrderNoBCD(byte[] orderNoBCD) {
            this.orderNoBCD = orderNoBCD;
        }

        /**
         * 获取订单号BCD码
         * @return 订单号BCD码字节数组
         */
        public byte[] getOrderNoBCD() {
            return orderNoBCD != null ? orderNoBCD.clone() : new byte[8];
        }

        /**
         * 获取订单号字符串
         * @return 订单号
         */
        public String getOrderNoString() {
            if (orderNoBCD == null) {
                return "";
            }
            return BcdUtil.bcdToString(orderNoBCD);
        }

        @Override
        public String toString() {
            byte[] address = getAddress();
            return "RechargeMessage.Downlink{" +
                    "control=0x" + String.format("%02X", getControl()) +
                    ", afn=0x" + String.format("%02X", getAfn()) +
                    ", address=" + (address != null ? bytesToHex(address) : "null") +
                    ", statusCode=0x" + String.format("%02X", getStatusCodeBCD()) +
                    ", balance=" + getBalance() +
                    ", username=" + getUsername() +
                    ", orderNo=" + getOrderNoString() +
                    '}';
        }
    }

    /**
     * 供内部使用的构造函数
     * @param control 控制域
     * @param address 地址域
     * @param userData 用户数据
     */
    protected RechargeMessage(byte control, byte[] address, byte[] userData) {
        super(control, address, FrameConstants.AFN_RECHARGE, userData);
    }

    /**
     * 将字节数组转换为十六进制字符串表示
     * @param bytes 字节数组
     * @return 十六进制字符串
     */
    private static String bytesToHex(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return "[]";
        }

        StringBuilder sb = new StringBuilder();
        sb.append("[");
        for (int i = 0; i < bytes.length; i++) {
            if (i > 0) {
                sb.append(" ");
            }
            sb.append(String.format("%02X", bytes[i] & 0xFF));
        }
        sb.append("]");
        return sb.toString();
    }
}
